#define STRICT
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <windowsx.h>
#include <ole2.h>
#include <commctrl.h>
#include <shlwapi.h>
#include <shlobj.h>
#include <shellapi.h>
#include "resource.h"

HINSTANCE g_hinst;

INT_PTR CALLBACK	TestDlgProc(HWND, UINT, WPARAM, LPARAM);
void CreateToolTipForRect(HWND hwndParent);
HWND CreateToolTip(int toolID, HWND hDlg, HWND hwndTip, PTSTR pszText);

class Window
{
public:
	HWND GetHWND() { return m_hwnd; }
protected:
	virtual LRESULT HandleMessage(
		UINT uMsg, WPARAM wParam, LPARAM lParam);
	virtual void PaintContent(PAINTSTRUCT *pps) { }
	virtual LPCTSTR ClassName() = 0;
	virtual BOOL WinRegisterClass(WNDCLASS *pwc)
	{ return RegisterClass(pwc); }
	virtual ~Window() { }

	HWND WinCreateWindow(DWORD dwExStyle, LPCTSTR pszName,
		DWORD dwStyle, int x, int y, int cx, int cy,
		HWND hwndParent, HMENU hmenu)
	{
		Register();
		HWND hWnd = CreateWindowEx(dwExStyle, ClassName(), pszName, dwStyle,
			x, y, cx, cy, hwndParent, hmenu, g_hinst, this);
		HWND hDlg = CreateDialog(g_hinst, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, TestDlgProc);
		ShowWindow(hDlg, SW_SHOW);
		return hWnd;
	}
private:
	void Register();
	void OnPaint();
	void OnPrintClient(HDC hdc);
	static LRESULT CALLBACK s_WndProc(HWND hwnd,
		UINT uMsg, WPARAM wParam, LPARAM lParam);
protected: 
	HWND m_hwnd;
};

INT_PTR CALLBACK	TestDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		{
			// Create the tooltip. g_hInst is the global instance handle.
			HWND hwndTip = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL,
				WS_POPUP |TTS_ALWAYSTIP,
				CW_USEDEFAULT, CW_USEDEFAULT,
				CW_USEDEFAULT, CW_USEDEFAULT,
				hDlg, NULL, 
				g_hinst, NULL);
			CreateToolTip(IDC_RADIO1, hDlg, hwndTip, TEXT("RadioTooltip First!!!"));
			CreateToolTip(IDC_RADIO2, hDlg, hwndTip, TEXT("RadioTooltip Second!!!"));
		}
		break;
	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}

void CreateToolTipForRect(HWND hwndParent)
{
    // Create a tooltip.
    HWND hwndTT = CreateWindowEx(WS_EX_TOPMOST,
        TOOLTIPS_CLASS, NULL,
        WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,		
        CW_USEDEFAULT, CW_USEDEFAULT,
        CW_USEDEFAULT, CW_USEDEFAULT,
        hwndParent, NULL, g_hinst,NULL);

    SetWindowPos(hwndTT, HWND_TOPMOST,
        0, 0, 0, 0,
        SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);

    // Set up "tool" information.
    // In this case, the "tool" is the entire parent window.
    TOOLINFO ti = { 0 };
    ti.cbSize = sizeof(TOOLINFO);
    ti.uFlags = TTF_SUBCLASS;
    ti.hwnd = hwndParent;
    ti.hinst = g_hinst;
    ti.lpszText = TEXT("This is your tooltip string.");;
    GetClientRect (hwndParent, &ti.rect);

    // Associate the tooltip with the "tool" window.
    SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);	
} 

HWND CreateToolTip(int toolID, HWND hDlg, HWND hwndTip, PTSTR pszText)
{
    if (!toolID || !hDlg || !pszText)
    {
        return FALSE;
    }
    // Get the window of the tool.
    HWND hwndTool = GetDlgItem(hDlg, toolID);
    
   if (!hwndTool || !hwndTip)
   {
       return (HWND)NULL;
   }                              
                              
    // Associate the tooltip with the tool.
    TOOLINFO toolInfo = { 0 };
    toolInfo.cbSize = sizeof(toolInfo);
    toolInfo.hwnd = hDlg;
    toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
    toolInfo.uId = (UINT_PTR)hwndTool;
    toolInfo.lpszText = pszText;
    SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);

    return hwndTip;
}

void Window::Register()
{
	WNDCLASS wc;
	wc.style         = 0;
	wc.lpfnWndProc   = Window::s_WndProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = g_hinst;
	wc.hIcon         = NULL;
	wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = ClassName();

	WinRegisterClass(&wc);
}

LRESULT CALLBACK Window::s_WndProc(
								   HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	Window *self;
	if (uMsg == WM_NCCREATE) {
		LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
		self = reinterpret_cast<Window *>(lpcs->lpCreateParams);
		self->m_hwnd = hwnd;
		SetWindowLongPtr(hwnd, GWLP_USERDATA,
			reinterpret_cast<LPARAM>(self));
	} else {
		self = reinterpret_cast<Window *>
			(GetWindowLongPtr(hwnd, GWLP_USERDATA));
	}
	if (self) {
		return self->HandleMessage(uMsg, wParam, lParam);
	} else {
		return DefWindowProc(hwnd, uMsg, wParam, lParam);
	}
}

LRESULT Window::HandleMessage(
							  UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	LRESULT lres;

	switch (uMsg) {
case WM_NCDESTROY:
	lres = DefWindowProc(m_hwnd, uMsg, wParam, lParam);
	SetWindowLongPtr(m_hwnd, GWLP_USERDATA, 0);
	delete this;
	return lres;

case WM_PAINT:
	OnPaint();
	return 0;

case WM_PRINTCLIENT:
	OnPrintClient(reinterpret_cast<HDC>(wParam));
	return 0;
case WM_INITDIALOG:
	CreateToolTipForRect(m_hwnd);
	}

	return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
}

void Window::OnPaint()
{
	PAINTSTRUCT ps;
	BeginPaint(m_hwnd, &ps);
	PaintContent(&ps);
	EndPaint(m_hwnd, &ps);
}

void Window::OnPrintClient(HDC hdc)
{
	PAINTSTRUCT ps;
	ps.hdc = hdc;
	GetClientRect(m_hwnd, &ps.rcPaint);
	PaintContent(&ps);
}

class RootWindow : public Window
{
public:
	virtual LPCTSTR ClassName() { return TEXT("Scratch"); }
	static RootWindow *Create();
protected:
	LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
	LRESULT OnCreate();
private:
	HWND m_hwndChild;
};

LRESULT RootWindow::OnCreate()
{
	return 0;
}

LRESULT RootWindow::HandleMessage(
								  UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg) {
case WM_CREATE:
	return OnCreate();  

case WM_NCDESTROY:
	// Death of the root window ends the thread
	PostQuitMessage(0);
	break;

case WM_SIZE:
	if (m_hwndChild) {
		SetWindowPos(m_hwndChild, NULL, 0, 0,
			GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
			SWP_NOZORDER | SWP_NOACTIVATE);
	}
	return 0;

case WM_SETFOCUS:
	if (m_hwndChild) {
		SetFocus(m_hwndChild);
	}
	return 0;
	}

	return __super::HandleMessage(uMsg, wParam, lParam);
}

RootWindow *RootWindow::Create()
{
	RootWindow *self = new RootWindow();
	if (self && self->WinCreateWindow(0,
		TEXT("Scratch"), WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL)) {
			return self;
	}
	delete self;
	return NULL;
}

int __stdcall
WinMain(HINSTANCE hinst, HINSTANCE, LPSTR, int nShowCmd)
{
	g_hinst = hinst;

	if (SUCCEEDED(CoInitialize(NULL))) {
		InitCommonControls();

		RootWindow *prw = RootWindow::Create();
		CreateToolTipForRect(prw->GetHWND());
		if (prw) {
			ShowWindow(prw->GetHWND(), nShowCmd);
			MSG msg;
			while (GetMessage(&msg, NULL, 0, 0)) {
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
		CoUninitialize();
	}
	return 0;
}
